package org.apache.lucene.distributed.cl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;

import org.apache.commons.io.IOUtils;
import org.apache.lucene.distributed.RMIClasses;
import org.apache.lucene.distributed.RMIClasses.ClassKey;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.SerialVersionUIDAdder;

// TODO: use resolver to resolve all subclasses of classes found by depends
public class JarFileCreator {
  public final Set<ClassKey> classKeys;
  ClassLoader classLoader;
  public byte[] digest;
  Set<String> rootClasses;
  
  public JarFileCreator(Depends depends) {
    classKeys = depends.classKeys;
    rootClasses = depends.rootClasses;
    classLoader = Thread.currentThread().getContextClassLoader();
  }
  
  public void resolveClasses() {
    
  }
  
  public static class ClassKeyComparator implements Comparator<ClassKey> {
    public int compare(ClassKey o1, ClassKey o2) {
      return o1.name.compareTo(o2.name);
    }
  }
  
  public void create(File file) throws Exception {
    MessageDigest md = MessageDigest.getInstance("MD5");
    
    List<String> rcList = new ArrayList<String>(rootClasses);
    Collections.sort(rcList);
    for (String s : rcList) 
      md.update(s.getBytes("UTF-8"));
    
    List<ClassKey> clList = new ArrayList<ClassKey>(classKeys);
    Collections.sort(clList, new ClassKeyComparator());
    JarOutputStream output = new JarOutputStream(new FileOutputStream(file));
    for (ClassKey classKey : clList) {
      String resource = RMIClasses.classToResource(classKey.name);
      JarEntry entry = new JarEntry(resource);
      output.putNextEntry(entry);
      byte[] classBytes = getClassBytes(classKey.name);
      if (classKey.serialUID == null) {
        classBytes = addSerialUID(classBytes, classKey);
        md.update(classKey.name.getBytes("UTF-8"));
        md.update(Long.toString(classKey.serialUID).getBytes("UTF-8"));
        md.update(classBytes);
      }
      output.write(classBytes);
      output.closeEntry();
    }
    output.close();
    digest = md.digest();
  }

  public static void main(String[] args) throws Exception {
    String name = "org.apache.lucene.search.highlight.SimpleFragmenter";
    String r = RMIClasses.classToResource(name);
    byte[] bytes = IOUtils.toByteArray(Thread.currentThread().getContextClassLoader().getResourceAsStream(r));
    byte[] newBytes = addSerialUID(bytes, null);
    Long l = ClassDepends.getSerialVersionUID(name, newBytes);
    System.out.println(l + "");
  }

  public byte[] getClassBytes(String name) throws IOException {
    return IOUtils.toByteArray(classLoader.getResourceAsStream(RMIClasses.classToResource(name)));
  }

  public static byte[] addSerialUID(byte[] bytes, ClassKey classKey) {
    ClassWriter classWriter = new ClassWriter(0);
    IDAdder sv = new IDAdder(classWriter);
    if (classKey != null) classKey.serialUID = sv.id;
    ClassVisitor ca = new ClassAdapter(sv);
    new ClassReader(bytes).accept(ca, 0);
    return classWriter.toByteArray();
  }
  
  public static class IDAdder extends SerialVersionUIDAdder {
    public long id;
    
    public IDAdder(ClassWriter cw) {
      super(cw);
    }
    
    protected long computeSVUID() throws IOException {
      id = super.computeSVUID();
      return id;
    }
  }
}
